package com.qf.websocket.handler;

/*
 *  @author Administrator
 *  @version V1.0
 *  @Project smartdeviceplatgorm
 *  @Package com.qf.websocket.handler
 *  @Description
 *  @Date2022/3/24 0024 11:09
 */


import com.qf.constants.DeviceStaus;
import com.qf.event.DevicePowerEvent;
import com.qf.event.DeviceStausCheckEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class PowerCommandHandler extends TextWebSocketHandler {
    private ApplicationContext context;
    private ConcurrentHashMap<String, WebSocketSession> allClients = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Long> lastHeartBeatMap = new ConcurrentHashMap<>();
    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    //连接建立
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.err.println("有设备连接了");
        URI uri = session.getUri();
        String uriString = uri.toString();//获取到字符串格式的地址,地址的最后一位是标识
        String deviceKey = uriString.substring(uriString.lastIndexOf("/") + 1);
        session.getAttributes().put("deviceKey", deviceKey);
        //检查当前标识之前有没有连接过
        WebSocketSession socketSession = allClients.get(deviceKey);
        if (socketSession != null) {
            socketSession.close();            //之前连接过,关闭之前的连接,替换为新的连接
        }
        //保存会话
        allClients.put(deviceKey, session);
        lastHeartBeatMap.put(deviceKey, System.currentTimeMillis());
        //设备连接之后,在之类更新状态就行了
        //我们要保证单一职责,更新设备状态的操作和我们当前websocket没关系,我们的websocket主要负责记录设备,主动给设备推送数据
        //交给专门更新的人处理,可以通过事件发送出去, 谁接收谁处理, 谁处理谁接收
        //我们发送事件的目的是更新某个设备为在线状态,并且记录连接的ip和区域,我们需要这些数据,
        //其中设备的编号,ip只能在这边获取到,所以我们需要通过事件携带出去
        context.publishEvent(new DevicePowerEvent(deviceKey, session.getRemoteAddress().getHostString(), DeviceStaus.ONLINE));

    }
    //收到消息
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.err.println("收到消息"+message.getPayload());
        //记录当前设备最后一次给我们发送数据的时间, 当前设备, 第二个是最后一次时间,典型的key-value
        Object deviceKey = session.getAttributes().get("deviceKey");
        //记录最后的时间,用于心跳检查用
        lastHeartBeatMap.put((String) deviceKey, System.currentTimeMillis());
    }
    //连接关闭
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.err.println("有设备离线了");
        //获取到标识
        Object deviceKey = session.getAttributes().get("deviceKey");
        //从allcients移除
        allClients.remove(deviceKey);
        //更新状态为离线
        context.publishEvent(new DevicePowerEvent((String) deviceKey, session.getRemoteAddress().getHostString(), DeviceStaus.OFFLINE));
    }

    @EventListener
    public void onEven(DeviceStausCheckEvent event) {
        System.err.println("设备心跳检查开始了");
        allClients.entrySet().parallelStream().forEach(stringWebSocketSessionEntry -> {
            WebSocketSession webSocketSession = stringWebSocketSessionEntry.getValue();
            String deviceId = stringWebSocketSessionEntry.getKey();//设备标识
            Long lastHeartTime = lastHeartBeatMap.get(deviceId);//获取最后一次心跳时间
            if (System.currentTimeMillis() - lastHeartTime > 30000) {
                //当前时间减去最后一次心跳时间大于30秒,我们就认为你断线了
                //断线做三个事情, 一个是关闭session, 一个是从allclients移除设备, 一个是从心跳的map中移除设备
                //遍历的时候能直接删除数据吗?
                if (webSocketSession.isOpen()) {
                    try {
                        webSocketSession.close();//关闭,触发上面的关闭监听,从map中会移除数据
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                lastHeartBeatMap.remove(deviceId);
            }
        });
    }
}
